// 海康威视摄像头相关
import Base64 from 'crypto-js/enc-base64';
import { MD5, HmacSHA256 } from 'crypto-js';
import { get } from 'lodash';


interface HikCameraSDKConfig {
    readonly appKey: string;
    readonly appSecret: string;
    readonly host: string;
}

interface HikCameraSDKRequest {
    method: string;
    url: string;
    headers?: {
        [name: string]: string;
    };
    data?: any;
}

// 签名所需的 headers
interface HikCameraSDKSignatureHeaders {
    'Content-Type': string;
    'X-Ca-Key': string;
    'X-Ca-Signature': string;
    'X-Ca-Signature-Headers': string;
    'X-Requested-With': string;
    'X-Ca-Timestamp': string;
    'X-Ca-Nonce': string;
    'Accept': string;
    [name: string]: string;
}


type HikCameraSDKSignatureHeadersMap<T extends string> = Map<keyof HikCameraSDKSignatureHeaders | T, string>;

class HikCameraSDK {
    private static instance: HikCameraSDK;

    private config: HikCameraSDKConfig;


    public static getInstance(): HikCameraSDK {
        if (!this.instance) {
            this.instance = new HikCameraSDK();
        }
        return this.instance;
    }

    private constructor() {
        this.config = {
            appKey: '24683405',
            appSecret: 'uVUno0quJGt4TG5l3V9I',
            host: 'https://191.244.13.50:443'
        }
    }

    public postSign(request: HikCameraSDKRequest): {
        url: string;
        headers: HikCameraSDKSignatureHeadersMap<any>;
    } {
        const headers = this.headersToSign(request);
        return {
            url: this.config.host + request.url,
            headers,
        }
    }

    private headersToSign(request: HikCameraSDKRequest): HikCameraSDKSignatureHeadersMap<any> {
        const headers = new Map();
        for (let name in request.headers) {
            name = name.toLowerCase();
            if (!name.startsWith('x-ca-')) {
                continue;
            }

            if (name === "x-ca-signature" || name === "x-ca-signature-headers" || name == "x-ca-key" || name === 'x-ca-nonce') {
                continue;
            }
            const value: any = get(request, 'headers.name');
            headers.set(name, value);
        }
        const timestamp = new Date().getTime();
        const nonce = this.createUuid();
        const contentType = get(request.headers || {}, "Content-Type") || 'application/json';
        const accept = get(request.headers || {}, "Accept") || '*/*';

        let textToSign = "";
        textToSign += request.method + "\n";
        textToSign += accept + "\n";
        textToSign += contentType + "\n";
        textToSign += request.url;

        // console.log("textToSign\n" + textToSign.replace(/\n/g, "#"));
        const hash = HmacSHA256(textToSign, this.config.appSecret)
        // console.log("hash:" + hash)
        const signature = hash.toString(Base64)
        // console.log("signature:" + signature)


        headers.set('Content-Type', contentType);
        headers.set('X-Ca-Key', this.config.appKey);
        headers.set('X-Ca-Timestamp', timestamp);
        headers.set('X-Ca-Nonce', nonce);
        headers.set('X-Ca-Signature', signature);
        headers.set('X-Requested-With', 'XMLHttpRequest');
        headers.set('Accept', accept);
        headers.set('X-Ca-Signature-Headers', null);



        // console.log("headers: ", headers.size, headers);

        // for (const key in headers) {
        //     console.log("key:" + key + "value:" + get(headers, key));
        // }
        return headers;
    }

    private calcMd5(request: HikCameraSDKRequest): string {
        const contentType = get(request.headers || {}, "Content-Type") || 'application/json';
        if (request.data && !contentType.startsWith('application/x-www-form-urlencoded')) {
            const data = request.data;
            const md5 = MD5(data);
            const md5String = md5.toString(Base64);
            // console.log("data:" + data + "\nmd5:" + md5String);
            return md5String;
        } else {
            return "";
        }
    }

    private createUuid(): string {
        return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
            const r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
            return v.toString(16);
        });
    }

    private urlToSign(request: HikCameraSDKRequest): string {
        const params = new Map();
        const contentType = get(request.headers || {}, "Content-Type") || 'application/json';
        if (contentType && contentType.startsWith('application/x-www-form-urlencoded') && request.data) {
            const formParams = request.data.split("&");
            formParams.forEach((p: any) => {
                const ss = p.split('=');
                params.set(ss[0], ss[1]);
            })
        }

        const ss = request.url.split('?');
        if (ss.length > 1 && ss[1]) {
            const queryParams = ss[1].split('&');
            queryParams.forEach((p) => {
                const ss = p.split('=');
                params.set(ss[0], ss[1]);
            })
        }

        const sortedKeys = Array.from(params.keys())
        sortedKeys.sort();


        const l1 = ss[0].lastIndexOf('/');
        const url = ss[0].substring(23);
        const first = true;
        let qs
        for (const k of sortedKeys) {
            const s = k + "=" + params.get(k);
            qs = qs ? qs + "&" + s : s;
            // console.log("key=" + k + " value=" + params.get(k));
        }
        return qs ? url + "?" + qs : url;
    }
}

export default HikCameraSDK;